<!doctype html>
<html lang="en-us">
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>ffdec.js</title>
  <!--    <script type="text/javascript" src="/jslib/jquery-3.5.1.min.js"></script>-->
  <script type="text/javascript" src="jquery.min.js"></script>
<body>

<div>
  <p>
    <input type="button" id="playTest" value="播放测试数据"/>
    <input type="file" id="playFile" accept="application/*"/>
  </p>
</div>

<div>
  <canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1 width="960" height="540"></canvas>

  <!-- 顶点Shader -->
  <script id="vertexShader" type="x-shader/x-vertex">
        precision mediump float;
        attribute vec4 vertex_screen;
        attribute vec2 vertex_texture;
        varying vec2 texture_pos;
        void main(void)
        {
          gl_Position = vertex_screen;
          texture_pos = vertex_texture;
        }

  </script>

  <!-- 片段Shader -->
  <script id="fragmentShader" type="x-shader/x-fragment">
        precision mediump float;
        varying vec2 texture_pos;
        uniform sampler2D texture_y;
        uniform sampler2D texture_u;
        uniform sampler2D texture_v;
        void main(void)
        {
          vec3 yuv;
          vec3 rgb;
          yuv.x = texture2D(texture_y, texture_pos).x;
          yuv.y = texture2D(texture_u, texture_pos).x-0.5;
          yuv.z = texture2D(texture_v, texture_pos).y-0.5;
          rgb = mat3(1,1,1,
                    0,-0.39465,2.03211,
                    1.13983,-0.58060,0) * yuv;
          gl_FragColor = vec4(rgb, 1);
        }

  </script>
</div>

<script type="text/javascript" src="src/assets/ffdec-1.0.0.min.js"></script>
<script type='text/javascript'>
  $(document).ready(function () {
    var Module = ffdecjs

    // 创建glsl程序
    var createProgramFromScripts = function (gl, vertex, fragment) {
      var create_shader = function (t, source) {
        //console.log("create_shader", t, source);
        var shader = gl.createShader(t); // gl.VERTEX_SHADER, gl.FRAGMENT_SHADER
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        // glGetShaderiv, compiled
        // TODO. error?

        return shader;
      };

      var program = gl.createProgram();

      var shader1 = create_shader(gl.VERTEX_SHADER, vertex);
      var shader2 = create_shader(gl.FRAGMENT_SHADER, fragment);

      gl.attachShader(program, shader1);
      gl.attachShader(program, shader2);

      gl.linkProgram(program);

      return program;
    }

    var canvas = document.getElementById('canvas');
    var gl = canvas.getContext('experimental-webgl');

    var program = createProgramFromScripts(gl, $('#vertexShader').text(), $('#fragmentShader').text());
    gl.useProgram(program);

    // 从glsl中获取参数
    var texture_y = gl.getUniformLocation(program, 'texture_y');
    var texture_u = gl.getUniformLocation(program, 'texture_u');
    var texture_v = gl.getUniformLocation(program, 'texture_v');

    var vertex_screen = gl.getAttribLocation(program, 'vertex_screen');
    var vertex_tex = gl.getAttribLocation(program, 'vertex_texture');

    // 创建y,u,v纹理
    var tex_id_y = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex_id_y);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    var tex_id_u = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex_id_u);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    var tex_id_v = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex_id_v);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

    // 纹理顶点(前8); 屏幕顶点(后8)
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,
        -1.0, -1.0,
        1.0, -1.0,
        -1.0, 1.0,
        1.0, 1.0]),
      gl.STATIC_DRAW);

    gl.bindBuffer(gl.ARRAY_BUFFER, null);

    gl.validateProgram(program);

    var draw_begin = function () {
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.clearColor(0.3, 0.3, 0.3, 1.0);
    }

    var draw_end = function () {

    }

    var draw_yuv = function (frame) {
      var w = frame.w;
      var h = frame.h;
      var y = frame.y;
      var u = frame.u;
      var v = frame.v;
      var pitch_y = frame.pitch_y;
      var pitch_u = frame.pitch_u;
      var pitch_v = frame.pitch_v;

      gl.useProgram(program);
      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

      // 顶点
      gl.vertexAttribPointer(vertex_screen, 2, gl.FLOAT, false, 8, 32);
      gl.enableVertexAttribArray(vertex_screen)
      gl.vertexAttribPointer(vertex_tex, 2, gl.FLOAT, false, 8, 0);
      gl.enableVertexAttribArray(vertex_tex)

      // Y
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, tex_id_y);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, pitch_y, h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, y);
      gl.uniform1i(texture_y, 0);

      // U
      gl.activeTexture(gl.TEXTURE1);
      gl.bindTexture(gl.TEXTURE_2D, tex_id_u);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, pitch_u, h / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, u);
      gl.uniform1i(texture_u, 1);

      // V
      gl.activeTexture(gl.TEXTURE2);
      gl.bindTexture(gl.TEXTURE_2D, tex_id_v);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, pitch_v, h / 2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, v);
      gl.uniform1i(texture_v, 2);

      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

      gl.disableVertexAttribArray(vertex_tex);
      gl.disableVertexAttribArray(vertex_screen);
      gl.bindBuffer(gl.ARRAY_BUFFER, null);
      gl.validateProgram(program);
    }

    ffdecjs.postRun = function () {
      ffdecjs.hello(1000);

      FS.mkdir('/data'); // 创建"/data"目录, 后面都向这个目录写文件
      ffdecjs.write_test_file('/data/test.flv'); // 写测试demo文件
      //ffdecjs.print_file('/data/test.flv');

      ffdecjs.open('/data/test.flv', '');

      setInterval(function () {
        var frame = ffdecjs.get_next_frame();
        //var frame = yuv_init();
        if (frame) {
          //gl.viewport(x, y, width, height); // 这里根据图像宽高与显示区域宽高,调整输出图像位置
          draw_begin();
          draw_yuv(frame);
          draw_end();
        }
        //console.log("frame:", frame);
        // frame.time = 0x8000000000000000 为无效值
      }, 33); // 可依据帧时间戳来控制播放节奏

      //ffdecjs.close();

      // 播放测试数据按钮
      $("#playTest").click(function () {
        ffdecjs.open('/data/test.flv', '');
      });

      // 选择文件播放
      $("#playFile").change(function () {
        console.log("play file onchange");
        var file0 = event.target.files[0];
        if (!!file0) {
          var reader = new FileReader();
          reader.readAsArrayBuffer(file0);
          reader.onload = function () {
            // TODO. 调整为分步读取数据, 写入文件, 避免一次读取过多的数据量
            var binary = new Uint8Array(this.result);
            //console.log("binary", binary);
            var stream = FS.open('/data/xxxx', 'w+');
            FS.write(stream, binary, 0, binary.length);
            FS.close(stream);
            //ffdecjs.print_file('/data/playFile.mp4');

            ffdecjs.open('/data/xxxx', '');
          }
        }
      });
    };
  });
</script>
</body>
</html>
